//
//  ME11Swiper.m
//  HPZZTong
//
//  Created by WenJun on 14/11/11.
//  Copyright (c) 2014年 handpay. All rights reserved.
//

#import "ME11Swiper.h"
#import "NLDump.h"
#import <MESDK/MESDK.h>
#import <MESDK/NLISOUtils.h>
#import <MESDK/NLAudioPortV100ConnParams.h>
#import <MESDK/NLDeviceLaunchEvent.h>
#import <MESDK/NLAudioPortHelper.h>
#import "GTMStringEncoding.h"
//#import "ZZTongClientEngine.h"
//#import "Utility.h"
//#import "HPClientEngine.h"
// marco add
#import "SwipeInfo.h"
#import "POSProcess.h"
#import "NSString+hexCodec.h"

#ifndef KeyKeyIndex
    #define KeyKeyIndex     5
    #define PinKeyIndex     2
    #define MacKeyIndex     3
    #define TrackKeyIndex   4
#endif

static ME11Swiper * singleton = nil;

@interface ME11Swiper()<NLDeviceEventListener,NLEmvControllerListener,NLAudioPortListener>
{
    id<NLDeviceDriver> _driver;
    SwipeInfo * _emvSwipeInfo;
    NSData * _currentDynamicKey;
    NSString * _dcData;
    NSString * _icNumber;
}


@property (strong, nonatomic) id<NLDevice> device;

@end

@implementation ME11Swiper

+ (id)sharedInstance
{
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        singleton = [[ME11Swiper alloc] init];
    });
    return singleton;
}

- (id)init
{
    self = [super initWithSwiperQueue:dispatch_queue_create("ME11", 0) wrapperQueue:dispatch_queue_create("ME11Wrapper", 0)];
    return self;
}

- (void)releaseInstance
{
    dispatch_async(self.swiperQueue,^{
        [NLAudioPortHelper unregisterAudioPortListener];
        [_device destroy];
        _device = nil;
    });
}

- (void)activeInstance
{
    dispatch_async(self.swiperQueue, ^{
        _driver = [[NLMESeriesDriver alloc] init];
        [NLAudioPortHelper registerAudioPortListener:self];
    });
}

// 获取KSN
- (void)getKSN:(SwiperComplete)complete
{
    if (self.ksn.length > 0 && [self.device isAlive])
    {
        ExecuteSwiperBlock(complete, [HPSwiperResult successResult:self.ksn]);
    }
    else if (self.connectState >= SwiperStateDectedDevice)
    {
        [self connectStateChanged:SwiperStateConnecting];
        [self doOperation:^HPSwiperResult *{
            NSError *err = nil;
            self.device = [_driver connectWithConnParams:[[NLAudioPortV100ConnParams alloc] init] closedListener:self launchListener:self error:&err];
            if (self.device && !err)
            {
                id deviceInfo = [self.device deviceInfo];
                NSString * csn = [deviceInfo CSN];
                if (csn.length == 0)
                {
                    [self stopSwiper];
                    [_device destroy];
                    _device = nil;
                }
                self.ksn = csn;
            }
            if (self.ksn.length > 0)
            {
                [self connectStateChanged:SwiperStateConnected];
                return [HPSwiperResult successResult:self.ksn];
            }
            else
            {
                [self connectStateChanged:SwiperStateConnectFailed];
                return [HPSwiperResult failResultWithCode:HPSwiperResultFail];
            }
        } complete:complete];
    }
    else
    {
        ExecuteSwiperBlock(complete, [HPSwiperResult failResultWithCode:HPSwiperResultNotConnected]);
    }
}

// 关键数据加密
- (void)encryptKeyData:(NSString *)keyData factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self doOperation:^HPSwiperResult *{
        HPSwiperResult * result = [HPSwiperResult failResultWithMsg:nil];
        id<NLPinInput> pin = (id<NLPinInput>)[_device standardModuleWithModuleType:NLModuleTypeCommonPinInput];
        NSData * encryptResult = nil;
        NSData * data = [self keyData:keyData];
        @try {
            encryptResult = [pin encryptWithWorkingKey:[self workingKeyWithIndex:KeyKeyIndex factor:factor] encryptType:NLEncryptTypeDisperseECB input:data cbcInit:[self cbcInitData]];
        }
        @catch (NSException *exception) {
            encryptResult = nil;
        }
        if (encryptResult.length > 0)
        {
            result = [HPSwiperResult successResult:[self hexEncode:encryptResult]];
        }
        return result;
    } complete:complete];
}


- (void)encyptAmount:(NSString *)amount factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self doOperation:^HPSwiperResult *{
        HPSwiperResult * result = [HPSwiperResult failResultWithMsg:nil];
        id<NLPinInput> pin = (id<NLPinInput>)[_device standardModuleWithModuleType:NLModuleTypeCommonPinInput];
        NSData * encryptResult = nil;
        NSData * data = [self amountData:amount];
        @try {
            encryptResult = [pin encryptWithWorkingKey:[self workingKeyWithIndex:KeyKeyIndex factor:factor] encryptType:NLEncryptTypeDisperseECB input:data cbcInit:[self cbcInitData]];
        }
        @catch (NSException *exception) {
            encryptResult = nil;
        }
        if (encryptResult.length > 0)
        {
            result = [HPSwiperResult successResult:[self hexEncode:encryptResult]];
        }
        return result;
    } complete:complete];
}

// mac加密
- (void)encryptMac:(NSString *)macStr factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self doOperation:^HPSwiperResult *{
        HPSwiperResult * result = [HPSwiperResult failResultWithMsg:nil];
        id<NLPinInput> pin = (id<NLPinInput>)[_device standardModuleWithModuleType:NLModuleTypeCommonPinInput];
        NLMacResult * macResult = nil;
        NSData * data = [self macData:macStr];
        @try {
            macResult = [pin calcMacWithBigData:[self workingKeyWithIndex:MacKeyIndex factor:factor] input:data macAlgorithm:NLMacAlgorithmDisperseX99 pinManageType:NLPinManageTypeFIXED];
        }
        @catch (NSException *exception) {
            macResult = nil;
        }
        if (macResult.mac.length > 0)
        {
            NSData * macData = [NSData dataWithBytes:macResult.mac.bytes length:4];
            result = [HPSwiperResult successResult:[self hexEncode:macData]];
        }
        return result;
    } complete:complete];
}

- (void)encryptPin:(NSString *)plainPin factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self doOperation:^HPSwiperResult *{
        id<NLPinInput> pin = (id<NLPinInput>)[_device standardModuleWithModuleType:NLModuleTypeCommonPinInput];
        NLPinEncryptResult * encryptResult = [pin startPinInputWithoutKeyboard:[self workingKeyWithIndex:PinKeyIndex factor:factor] pinManageType:NLPinManageTypeFIXED acctInputType:NLAccountInputTypeUnuseAcct acctSymbol:@"00000000000000000000" inputMaxLen:6 pinPadding:[NSData fillWithByte:'F' len:10] pin:[plainPin dataUsingEncoding:NSUTF8StringEncoding]];
        return [HPSwiperResult successResult:[self hexEncode:encryptResult.encrypPin]];
    } complete:complete];
}

// 刷卡
- (void)swipe:(int)time factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self doOperation:^HPSwiperResult *{
        HPSwiperResult * result = [HPSwiperResult failResultWithMsg:nil];
        id<NLCardReader> cardReader = (id<NLCardReader>)[self.device standardModuleWithModuleType:NLModuleTypeCommonCardReader];
        int timeout = 60;
        _currentDynamicKey = [self genDynamicKey];
        NLME11SwipeResult *rslt = [cardReader openCardReader:@[@(NLModuleTypeCommonSwiper), @(NLModuleTypeCommonICCard)] readModel:@[@(NLSwiperReadModelReadSecondTrack),@(NLSwiperReadModelReadThirdTrack)] panType:0xFF encryptAlgorithm:[NLTrackEncryptAlgorithm BY_HANDPAY_MODEL] wk:[self workingKeyWithIndex:TrackKeyIndex factor:factor] time:[self hexDecode:[[factor substringToIndex:14] substringFromIndex:2]] random:_currentDynamicKey appendData:nil timeout:timeout];
        if (!rslt || rslt.rsltType != NLSwipeResultTypeSuccess || rslt.moduleTypes <= 0)
        {
            return result;
        }
        
        NLModuleType moduleType = [rslt.moduleTypes[0] intValue];
        if (NLModuleTypeCommonICCard == moduleType)
        {
            id<NLEmvModule> emvModule = (id<NLEmvModule>)[_device standardModuleWithModuleType:NLModuleTypeCommonEMV];
            id<NLEmvTransController> emvController = [emvModule emvTransControllerWithListener:self];
            [emvController startEmvWithAmount:[NSDecimalNumber decimalNumberWithString:@"10.00"] cashback:[NSDecimalNumber zero] forceOnline:YES];
            return nil;
        }
        else if (NLModuleTypeCommonSwiper == moduleType)
        {
            result = [self cardNoResultFromME11Result:rslt dynamicKey:_currentDynamicKey];
        }
        return result;
    } complete:complete];
}

- (void)onRequestOnline:(id<NLEmvTransController>)controller context:(NLEmvTransInfo*)context error:(NSError*)err
{
    SwipeInfo * swipeInfo = [[SwipeInfo alloc] init];
    swipeInfo.icMode = YES;
    swipeInfo.plainAccount = context.cardNo;
    _dcData = [self hexEncode:[self dcDataFromContext:context]];
    _icNumber = [self icNumberFromContext:context];
    swipeInfo.encTrack2 = [self hexEncode:context.track_2_eqv_data];
    swipeInfo.dynamicKey = [self hexEncode:_currentDynamicKey];
    //test
//    if (context.cardExpirationDate.length >= 4)
//    {
//        swipeInfo.plainExpireDate = [context.cardExpirationDate substringToIndex:4];
//    }
    [self operationFinishedWithResult:[[HPSwiperResult alloc] initWithCode:HPSwiperResultSuccess message:nil value:swipeInfo]];
}

- (void)emv:(POSProcess *)emvInfo complete:(SwiperComplete)complete
{
    emvInfo.swipeInfo.dcData = _dcData;
    emvInfo.swipeInfo.icNumber = _icNumber;
    ExecuteSwiperBlock(complete,[HPSwiperResult successResult:nil]);
}

- (void)onEmvFinished:(BOOL)isSuccess context:(NLEmvTransInfo*)context error:(NSError*)err
{
    [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultCancel]];
}

-(void)selectApp:(NSArray*)type time:(int)time factor:(NSString *)factor complete:(SwiperComplete)complete
{
    
}

- (void)onEvent:(id <NLDeviceEvent>)event
{
    if ([event isKindOfClass:[NLConnectionCloseEvent class]])
    {
    }
    else if ([event isKindOfClass:[NLDeviceLaunchEvent class]])
    {
    }
}

- (void)onDevicePlugged
{
    NSLog(@"onDevicePlugged");
    [self connectStateChanged:SwiperStateDectedDevice];
}

- (void)onDeviceUnplugged
{
    NSLog(@"onDeviceUnplugged");
    [self connectStateChanged:SwiperStateNoDeviceDected];
}

- (HPSwiperResult *)cardNoResultFromME11Result:(NLME11SwipeResult *)nlResult dynamicKey:(NSData *)dynamicKey
{
    HPSwiperResult * returnResult = [HPSwiperResult failResultWithMsg:nil];
    if (nlResult.rsltType == NLSwipeResultTypeSuccess && [nlResult.secondTrackData length] > 0)
    {
        SwipeInfo * swipeInfo = [[SwipeInfo alloc] init];
        swipeInfo.dynamicKey = [self hexEncode:dynamicKey]; //[dynamicKey encodeToHexString];
        swipeInfo.plainAccount = nlResult.account.acctId;
        swipeInfo.encTrack2 = [self hexEncode:nlResult.secondTrackData];//[nlResult.secondTrackData encodeToHexString];
        swipeInfo.icMode = NO;
        if(nlResult.serviceCode.length)
            swipeInfo.serviceCodePrefix = [nlResult.serviceCode substringToIndex:1];
        returnResult = [HPSwiperResult successResult:swipeInfo];
    }
    returnResult.userInfo = nlResult;
    return returnResult;
}

- (NSString *)icNumberFromContext:(NLEmvTransInfo *)context
{
    NSString * icNumber = context.cardSequenceNumber;
    if (icNumber.length > 0)
    {
        while (icNumber.length < 3)
        {
            icNumber = [NSString stringWithFormat:@"0%@",icNumber];
        }
    }
    else
    {
        return @"000";
    }
    return icNumber;
}

void appendToDCData(Byte * dcBytes, NSData * data, UInt8 tag1, UInt8 tag2, int * offset)
{
    if (data.length > 0)
    {
        dcBytes[*offset] = tag1;
        *offset += 1;
        if (tag2 > 0)
        {
            dcBytes[*offset] = tag2;
            *offset += 1;
        }
        dcBytes[*offset] = data.length;
        *offset += 1;
        memcpy(dcBytes + *offset, data.bytes, data.length);
        *offset += data.length;
    }
}

- (NSData *)dcDataFromContext:(NLEmvTransInfo *)context
{
    Byte dcBytes[256] = {0};
    int offset = 0;
    
    appendToDCData(dcBytes, context.applicationInterchangeProfile, 0x82, 0, &offset);
    appendToDCData(dcBytes, context.appTransactionCounter, 0x9f, 0x36, &offset);
    appendToDCData(dcBytes, context.appCryptogram, 0x9f, 0x26, &offset);
    
    dcBytes[offset] = 0x9f;
    dcBytes[offset + 1] = 0x27;
    dcBytes[offset + 2] = 0x01;
    dcBytes[offset + 3] = context.cryptogramInformationData;
    offset += 4;
    
    appendToDCData(dcBytes, context.terminal_capabilities, 0x9f, 0x33, &offset);
    appendToDCData(dcBytes, context.terminalVerificationResults, 0x95, 0, &offset);
    appendToDCData(dcBytes, context.unpredictableNumber, 0x9f, 0x37, &offset);
    
    NSString* moneyStr = [context.amountAuthorisedNumeric leftPaddingWithChart:@"0" resultStringLength:12];
    appendToDCData(dcBytes, [self hexDecode:moneyStr], 0x9f, 0x02, &offset);
    
    NSString * otherMoneyStr = [context.amountOtherNumeric leftPaddingWithChart:@"0" resultStringLength:12];
    appendToDCData(dcBytes, [self hexDecode:otherMoneyStr], 0x9f, 0x03, &offset);
    appendToDCData(dcBytes, [self hexDecode:context.terminalCountryCode], 0x9f, 0x1a, &offset);
    appendToDCData(dcBytes, [self hexDecode:context.transactionCurrencyCode], 0x5f, 0x2a, &offset);
    appendToDCData(dcBytes, [self hexDecode:context.transactionDate], 0x9a, 0, &offset);
    
    dcBytes[offset] = 0x9c;
    dcBytes[offset + 1] = 0x01;
    dcBytes[offset + 2] = context.transactionType;
    offset += 3;
    
    appendToDCData(dcBytes, context.issuerApplicationData, 0x9f, 0x10, &offset);
    
    return [NSData dataWithBytes:dcBytes length:offset];
}

- (NSData *)amountData:(NSString *)amount
{
    NSMutableData * amountData = [NSMutableData dataWithData:[self hexDecode:amount]];
    int more = 0;
    while ((amountData.length + more) % 8 != 0)
    {
        more++;
    }
    Byte bytes[more];
    memset(bytes, 0x00, more);
    [amountData appendBytes:bytes length:more];
    return amountData;
}

- (NSData *)macData:(NSString *)macString
{
    NSMutableData * macData = [NSMutableData dataWithBytes:[macString UTF8String] length:macString.length];
    Byte bytes[1] = {0x80};
    [macData appendBytes:bytes length:1];
    int more = 0;
    while ((macData.length + more) % 8 != 0)
    {
        more++;
    }
    Byte bytes2[more];
    memset(bytes2, 0x00, more);
    [macData appendBytes:bytes2 length:more];
    Byte bytes3[8] = {0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
    [macData appendBytes:bytes3 length:8];
    return macData;
}

- (NSData *)keyData:(NSString *)keyString
{
    NSMutableData * keyData = [NSMutableData dataWithBytes:[keyString UTF8String] length:keyString.length];
    int more = 0;
    while ((keyData.length + more) % 8 != 0)
    {
        more++;
    }
    Byte bytes[more];
    memset(bytes, 0x00, more);
    [keyData appendBytes:bytes length:more];
    return keyData;
}

- (BOOL)isConnected
{
    return self.ksn.length > 0 && [NLAudioPortHelper isDevicePresent];
}

- (void)disconnect
{
    dispatch_async(self.swiperQueue,^{
        [super disconnect];
        [_device destroy];
        _device = nil;
    });
}

- (NLWorkingKey *)workingKeyWithIndex:(int)index factor:(NSString *)factor
{
    NSData * timeData = [NLISOUtils hexStr2Data:factor];
    return [[NLWorkingKey alloc] initWithIndex:index wk:timeData];
}

- (NSData *)genDynamicKey
{
    u_int32_t data = 0;
    Byte *buf = (Byte*)malloc(8);
    for (int i = 0; i < 8; i ++)
    {
        if (data == 0)
        {
            data = arc4random();
        }
        buf[i] = data & 0x000000ff;
        data = data >> 8;
    }
    return [NSData dataWithBytes:buf length:8];
}

- (NSData *)cbcInitData
{
    Byte bytes[8] = {0};
    return [NSData dataWithBytes:bytes length:8];
}

- (NSString *)hexEncode:(NSData *)data
{
    return [[GTMStringEncoding hexStringEncoding] encode:data];
}

- (NSData *)hexDecode:(NSString *)string
{
    if (string.length % 2 == 1)
    {
        string = [NSString stringWithFormat:@"0%@",string];
    }
    return [[GTMStringEncoding hexStringEncoding] decode:string];
}

- (void)stopSwiper
{
    [super stopSwiper];
    [_device cancelCurrentExecute];
    [_device reset];
}

- (void)onRequestSelectApplication:(id<NLEmvTransController>)controller context:(NLEmvTransInfo*)context error:(NSError*)err{}
- (void)onRequestTransferConfirm:(id<NLEmvTransController>)controller context:(NLEmvTransInfo*)context error:(NSError*)err{}
- (void)onRequestPinEntry:(id<NLEmvTransController>)controller context:(NLEmvTransInfo*)context error:(NSError*)err{}
- (void)onFallback:(NLEmvTransInfo*)context error:(NSError*)err{}

- (void)onError:(id<NLEmvTransController>)controller error:(NSError*)err
{
    [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultEMVFailed]];
}

@end
